<template>
  <div class="table-box">
    <!--工具栏-->
    <div class="tool-bar" v-if="showQueryForm">
      <div class="form-tool">
        <el-form
          ref="queryForm"
          class="query-form"
          label-width="0px"
          :model="filters"
          :inline="true"
          @submit.prevent
        >
          <el-form-item
            v-for="item in filterFields"
            :key="item.name"
            :prop="item.prop"
          >
            <component
              :is="'ac-' + (item.type || 'input')"
              v-model="filters[item.prop]"
              v-bind="cleanProp(item, filters)"
              :placeholder="item.label"
              :title="item.label"
              @change="fieldValueChange"
              clearable
            />
          </el-form-item>
          <el-form-item v-if="!noCheckButton">
            <el-button
              type="primary"
              @click="queryPage($event)"
            ><ac-icon iconId="018dc9c3efe489a7d117" style="margin-right: 5px" />查询</el-button>
          </el-form-item>
          <el-form-item
            v-for="item in activeTableOperators"
            v-show="showOperators"
            :key="item.id"
          >
            <el-button
              :ref="'bn_' + item.id"
              :type="item.buttonClass"
              @click="clickTableButton($event, item)"
            ><ac-icon :iconId="item.buttonIcon" style="margin-right: 5px" />{{item.name}}</el-button>
          </el-form-item>
        </el-form>
      </div>
    </div>
    <el-row v-if="tableTitle !== ''">
      <el-col
        :span="24"
        style="text-align: center;font-size: 25px;font-weight:bolder;padding-top:10px"
      >
        <div class="table-title-box">{{tableTitle}}</div>
      </el-col>
    </el-row>
    <!--表格栏-->
    <el-table
      key="id"
      v-loading="loading"
      element-loading-text="数据加载中..."
      ref="elDataTable"
      :show-overflow-tooltip="false"
      class="data-table"
      width="100%"
      :data="tableDataList"
      :row-class-name="rowClassName"
      :default-sort="defaultSort"
      :border="border"
      :stripe="stripe"
      :cell-style="tableCellStyle"
      :cell-class-name="cellClass"
      :sumText="sumText"
      :showSummary="showSummary"
      :summaryMethod="summaryMethod"
      :span-method="spanMethod"
      :header-cell-style="headerCellStyle"
      @selection-change="selectionChange"
      @sort-change="sortChange"
      @row-click="clickRow"
      @row-dblclick="dblClickRow"
    >
      <template #empty>
        <div class="el-table__empty-block">
          <img src="@/assets/images/no-data.png" style="width: 100px" alt />
          <span style="min-width: 100px">暂无数据</span>
        </div>
      </template>
      <el-table-column
        v-if="props.showOperators || props.showCheckBox"
        header-align="center"
        align="center"
        type="selection"
        width="40"
        :show-overflow-tooltip="false"
      ></el-table-column>
      <el-table-column
        v-for="column in calcColumns"
        header-align="center"
        class-name="default-cell"
        :align="column.align !== undefined ? column.align : 'center'"
        :prop="column.prop"
        :label="column.label"
        :width="column.width"
        :min-width="column.minWidth||0"
        :key="column.prop"
        :type="['selection', 'index', 'expand'].includes(column.type) ? column.type : null"
        :formatter="column.formatter"
        :fixed="column.buttons === undefined ? column.fixed : 'right'"
        :sortable="column.buttons === undefined ? (column.sortable == null ? (pageSort ? '' : 'custom') : column.sortable) : false"
        :show-overflow-tooltip="{
          'effect': 'light',
          'raw-content': false,
          'placement': 'bottom',
          'popper-class': 'padding-10-15',
        }"
      >
        <template v-slot="scope">
          <span v-if="column.type === 'index'" style="width: 100%">
            {{ scope.$index + 1 }}
          </span>
          <span
            v-else-if="column.type === 'radio'
              || column.type === 'number'
              || column.type === 'switch'
              || column.type === 'select'
              || column.type === 'buttons'"
            class="overflow-ellipsis"
          >{{scope.row[column.prop]}}</span>
          <component
            v-else-if="column.type
              && column.type !== 'datetime'
              && (column.type !== 'image'
                || (column.type === 'image' && scope.row[column.prop]))"
            :is="'ac-' + column.type"
            v-model="scope.row[column.prop]"
            v-bind="cleanProp(column, scope.row)"
            showText
          />
          <div
            v-else-if="column.formatterHtml"
            :class="['overflow-ellipsis', column.click ? 'click-link' : '']"
            @click="column.click ? cellClick(column, scope.row) : () => {}"
            v-html="column.formatterHtml(column.prop, scope.row[column.prop], scope.row)"
          ></div>
          <div
            v-else
            :class="['overflow-ellipsis', column.click ? 'click-link' : '']"
            @click="column.click ? cellClick(column, scope.row) : () => {}"
          >
            {{
              column.formatter
                ? column.formatter(column.prop, scope.row[column.prop], scope.row)
                : column.formatNumber
                  ? tool.formatNumber(scope.row[column.prop], column.formatNumber)
                  : (column.type === 'datetime'
                    ? date.formatLongDate(scope.row[column.prop])
                    : scope.row[column.prop])
            }}
          </div>
        </template>
      </el-table-column>
      <el-table-column
        v-if="showOperators && activeRowOperators.length > 0"
        header-align="center"
        align="center"
        label="操作"
        fixed="right"
        :width="calcOpMinWidth"
        :show-overflow-tooltip="false"
      >
        <template v-slot="scope">
          <span
            v-for="item in activeRowOperators"
            :key="item.id"
            class="button-box"
          >
            <el-tooltip
              v-if="validRowOpShow(item, scope.row)"
              effect="light"
              placement="bottom"
              :enterable="false"
              :show-after="0"
              :hide-after="0"
              :content="item.name"
              popper-class="padding-10-15"
            >
              <el-button
                circle
                :type="item.buttonClass"
                @click.stop="clickRowButton($event, item, scope.row)"
              ><ac-icon :iconId="item.buttonIcon" /></el-button>
            </el-tooltip>
          </span>
        </template>
      </el-table-column>
      <template v-if="$slots.append" #append>
        <slot name="append"></slot>
      </template>
    </el-table>
    <!--分页栏-->
    <div
      v-if="showPagination"
      class="pagination-box"
    >
      <el-tooltip
        v-if="remark && remark.length > 0"
        class="item"
        effect="light"
        :content="remark"
        placement="top-start"
      >
        <div class="pagination-box-left">
          {{remark}}
        </div>
      </el-tooltip>
      <div class="pagination-box-right">
        <el-pagination
          layout="total, prev, pager, next, jumper, sizes"
          :current-page="pageRequest.pageNumber"
          :page-sizes="[10, 20, 50, 100, 500]"
          :page-size="pageRequest.pageSize"
          :total="data.totalRecord"
          @current-change="refreshPage"
          @size-change="handleSizeChange"
        />
      </div>
    </div>

    <!--新增/编辑/查看界面-->
    <div>
      <el-dialog
        v-model="fileVisible"
        width="800px"
        class="upload-dialog"
        :append-to-body="true"
        :title="fileTitle"
        :close-on-click-modal="false"
        destroy-on-close
        draggable
      >
        <el-form
          ref="dataForm"
          label-width="120px"
          label-position="right"
        >
          <el-row style="margin-top: 10px;">
            <el-col :span="12">
              <el-form-item label="文件选择" prop="file">
                <el-upload
                  ref="excelUpload"
                  action=""
                  accept=".xls, .xlsx"
                  :multiple="false"
                  :limit="1"
                  :http-request="handleUpload"
                  :show-file-list=false
                  :before-upload="handleBeforeUpload"
                >
                  <el-button :loading="fileLoading" type="primary">Excel数据导入</el-button>
                  <template #tip><div class="el-upload__tip">只能上传xls/xlsx文件</div></template>
                </el-upload>
              </el-form-item>
            </el-col>
            <el-col class="error-text" v-if="errorMsg !== ''" :span="24">
              <div v-html="errorMsg"></div>
            </el-col>
          </el-row>
        </el-form>
        <template #footer>
          <div class="dialog-footer">
            <el-button
              size="default"
              @click="fileVisible=false"
              type="info"
            >取消</el-button>
          </div>
        </template>
      </el-dialog>
    </div>
  </div>
</template>

<script setup>
import { ref, nextTick, watch, computed, onMounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { useRoute } from 'vue-router';
import { useRouterStore } from '@/admin/pinia/router';
import api from '@/http/api';
import date from "@/utils/date";
import tool from "@/utils/tool";

const emit = defineEmits([
  'data',
  'handleAfterQuery',
  'resetForm',
  'handleTableSelect',
  'clickRow',
  'handleTableOperator',
  'handleAjaxSuccess',
  'handleCellOperator',
])

const props = defineProps({
  columns: Array,
  filterFields: Array,
  pkName: {
    type: String,
    default: 'id'
  },
  showPagination: {
    type: Boolean,
    default: true
  },
  showOperators: {
    type: Boolean,
    default: true
  },
  showQueryForm: {
    type: Boolean,
    default: true
  },
  dataUrl: {
    type: String,
    default: ''
  },
  dataList: {
    type: Array,
    default: () => { return []; }
  },
  tableTitle: {
    type: String,
    default: ''
  },
  align: {
    type: String,
    default: 'left'
  },
  border: {
    type: Boolean,
    default: true
  },
  stripe: {
    type: Boolean,
    default: false
  },
  highlightCurrentRow: {
    type: Boolean,
    default: true
  },
  filters: Object,
  tableCellStyle: {
    type: Function ,
    default: function() {
      return {};
    }
  },
  cellClass:{
    type: Function ,
    default: function() {
      return {};
    }
  },
  defaultSort: {
    type: Object,
    default: () => {}
  },
  autoLoadPage: {
    type: Boolean,
    default: true
  },
  showCheckBox:{
    type: Boolean,
    default: false
  },
  opMinWidth: {
    type: Number,
    default: 120
  },
  pageSort:{
    type: Boolean,
    default: false
  },
  noCheckButton:{
    type: Boolean,
    default: false
  },
  sumText: {
    type: String,
    default: '合计'
  },
  headerCellStyle: Function,
  spanMethod: Function,
  showSummary: {
    type: Boolean,
    default: false
  },
  summaryMethod: Function,
  checkRowOpShow: {
    type: Function,
    default: null
  },
  tableRowClassName: {
    type: Function,
    default: () => { return '' }
  },
  remark: {
    type: String,
    default: ''
  },
  options: Object,
});

const route = useRoute();
const routerStore = useRouterStore();
const elDataTable = ref();
const excelUpload = ref();
const operators = ref([]);
const queryUrl = ref('');
const pageRequest = ref({
  pageNumber: 1,
  pageSize: props.filters && props.filters.pageSize || 20
});
const data = ref({
  pageNumber: 1,
  pageSize: 20,
  totalPage: 0,
  totalRecord: 0
});
const tableDataList = ref([]);
const loading = ref(false);
const selections = ref([]);
const uploadButton = ref({});
const fileTitle = ref('');
const fileVisible = ref(false);
const fileLoading = ref(false);
const errorMsg = ref('');
const cleanProps = ref(['prop', 'label', 'type', 'table', 'sortable', 'tooltip']);
const filters = ref(props.filters || {});

const cleanProp = (field, json) => {
  const result = {};
  for (let prop in field) {
    if (cleanProps.value.indexOf(prop) < 0) {
      if (typeof field[prop] === 'function') {
        result[prop] = field[prop](json);
      } else {
        result[prop] = field[prop];
      }
    }
  }
  return result;
};

const activeTableOperators = computed(() => {
  return operators.value.filter(operator => operator.id !== '0'
    && operator.buttonLevel && (operator.buttonLevel === '表级' || operator.buttonLevel === '全部'));
});

const activeRowOperators = computed(() => {
  return operators.value.filter(operator => operator.id !== '0'
    && operator.buttonLevel && (operator.buttonLevel === '行级' || operator.buttonLevel === '全部'));
});

const calcColumns = computed(() => {
  return props.columns.filter(column => column.table === undefined
    || column.table === true
    || (typeof column.table === 'function' && column.table.call() === true))
});

const calcOpMinWidth = computed(() => {
  return activeRowOperators.value.length > 2 && props.opMinWidth === 120
    ? activeRowOperators.value.length * 55 : props.opMinWidth;
});

const validRowOpShow = (op, row) => {
  if (!props.checkRowOpShow) {
    return true;
  } else {
    return props.checkRowOpShow(op, row);
  }
};

const fieldValueChange = () => {
  refreshPage(1);
};

const queryPage = (event) => {
  event.currentTarget.blur();
  refreshPage(1);
};

const refreshPage = (pageNumber) => {
  pageRequest.value.pageNumber = pageNumber;
  findPage();
};

const handleSizeChange = (pageSize) => {
  pageRequest.value.pageSize = pageSize
  refreshPage(1)
};

const sortChange = (column) => {
  if(props.pageSort) {
    return true;
  } else {
    if (column.order === 'ascending') {
      pageRequest.value.sortField = column.prop
      pageRequest.value.sortType = 'asc'
    } else if (column.order === 'descending') {
      pageRequest.value.sortField = column.prop
      pageRequest.value.sortType = 'desc'
    } else {
      pageRequest.value.sortField = null
      pageRequest.value.sortType = null
    }
    refreshPage(1);
  }
};

const findPage = () => {
  loading.value = true;
  api.post(queryUrl.value, Object.assign({}, props.filters, pageRequest.value)).then((res) => {
    data.value = res;
    tableDataList.value = res.data;
    pageRequest.value.pageSize = res.pageSize || pageRequest.value.pageSize;
    emit('data', res.data);
    emit('handleAfterQuery', res.data);
    loading.value = false;
  }).catch(() => {
    loading.value = false;
  })
};

const selectionChange = (selectionValues) => {
  selections.value = selectionValues;
  emit('handleTableSelect', selections.value);
}

const clickRow = (row) => {
  elDataTable.value.clearSelection()
  elDataTable.value.toggleRowSelection(row)
  emit('clickRow', row);
}

const dblClickRow = (row) => {
  const column = props.columns.find(item => item.click === true);
  if (column) {
    emit('handleCellOperator', column.prop, row[column.prop], row);
  }
}

const clearSelection = () => {
  elDataTable.value.clearSelection()
}

const rowClassName = (row) => {
  let rowName = ''
  if (props.highlightCurrentRow && selections.value.find(s => s[props.pkName] === row.row[props.pkName])) {
    rowName += "el-table__row--striped current-row";
  }

  rowName += ' ' + (props.tableRowClassName(row.row) || '');
  return rowName;
}

const clickTableButton = (event, operator) => {
  if (event) {
    event.currentTarget.blur();
  }

  let selectionValues = null
  if (operator.selectedMode === '2') {
    if (selections.value.length < 1) {
      ElMessage.warning('请选择一条记录！');
      return;
    } else if (selections.value.length > 1) {
      ElMessage.warning('只能选择一条记录！')
      return;
    }
    selectionValues = selections.value[0]
  } else if (operator.selectedMode === '3') {
    selectionValues = selections.value
  } else if (operator.selectedMode === '4') {
    if (selections.value.length < 1) {
      ElMessage.warning('请至少选择一条记录！');
      return;
    }
    selectionValues = selections.value;
  }
  if (operator.buttonType === 'delete' || operator.buttonType === 'ajax') {
    ajaxTodo(operator, selectionValues);
  } else if(operator.buttonType === 'downloadExcel') {
    downloadExcel(operator, selectionValues);
  } else if(operator.buttonType === 'importExcel') {
    importExcel(operator);
  } else {
    emit('handleTableOperator', Object.assign({clickType: 'table'}, operator), selections.value);
  }
}

const clickRowButton = (event, operator, row) => {
  event.currentTarget.blur();

  let selectionValues
  if (operator.selectedMode === '2') {
    selectionValues = row;
  } else if (operator.selectedMode === '3' || operator.selectedMode === '4') {
    selectionValues = [row];
  } else {
    selectionValues = row;
  }
  if (operator.buttonType === 'delete' || operator.buttonType === 'ajax') {
    ajaxTodo(operator, selectionValues);
  } else {
    emit('handleTableOperator', Object.assign({clickType: 'row'}, operator), selectionValues);
  }

  clearSelection();
  elDataTable.value.toggleRowSelection(row)
}

const downloadExcel = (operator,selections) => {
  if (operator.selectedMode === '3' || operator.selectedMode === '4') {
    const params = {}
    if (selections != null && selections.length > 0) {
      const ids = selections.map(item => item[props.pkName]).toString();
      params[props.pkName + 's'] = ids.split(',');
    }
    api.downloadFile(operator.dataDestUrl, params).then((res) => {
      try {
        api.saveFileLocal(res);
      } catch(err) {
        const enc = new TextDecoder('utf-8');
        const res1 = JSON.parse(enc.decode(new Uint8Array(res)));
        ElMessage.error(res1.message);
      }
    });
  } else {
    api.downloadFile(operator.dataDestUrl, props.filters).then((res) => {
      try {
        api.saveFileLocal(res);
      } catch(err) {
        const enc = new TextDecoder('utf-8');
        const res1 = JSON.parse(enc.decode(new Uint8Array(res)));
        ElMessage.error(res1.message);
      }
    });
  }
}

const importExcel = (operator) => {
  fileTitle.value = operator.name;
  fileVisible.value = true;
  uploadButton.value = operator;
  errorMsg.value = '';
}

const handleUpload = (file) => {
  return new Promise((resolve, reject) => {
    errorMsg.value = '';
    fileLoading.value = true;
    api.uploadFile(
      uploadButton.value.dataDestUrl,
      Object.assign({'file': file.file}, props.filters, props.options)
    ).then((res) => {
      fileLoading.value = false;
      if (res.code === '1') {
        fileVisible.value = false;
        ElMessage.success(res.message);
        findPage();
      } else {
        errorMsg.value = res.message.replace(/@@@/g, '<br/>');
      }
      resolve();
    }).catch(() => {
      fileLoading.value = false;
      reject();
    });

    excelUpload.value && excelUpload.value.clearFiles();
    resolve();
  });
}

const handleBeforeUpload = (file) => {
  const isLt10M = file.size / 1024 / 1024 < 10;
  if (!isLt10M) {
    ElMessage.error("上传文件大小不能超过 10MB!");
  }

  return isLt10M;
}

const ajaxTodo = (operator, selections) => {
  ElMessageBox.confirm((operator.clickTips || '数据提交，请确认？'), '提示', {type: 'warning'}).then(() => {
    const params = {}
    if (operator.selectedMode === '2') {
      params[props.pkName] = selections[props.pkName];
    } else if (operator.selectedMode === '3' || operator.selectedMode === '4') {
      if (selections != null && selections.length > 0) {
        const ids = selections.map(item => item[props.pkName]).toString();
        params[props.pkName + 's'] = ids.split(',');
      }
    }
    loading.value = true;
    api.post(operator.dataDestUrl, params).then(res => {
      ElMessage.success(res.message);
      findPage();
      loading.value = false;
      emit('handleAjaxSuccess', operator, selections);
    }).catch(() => {
      loading.value = false;
    })
  }).catch(() => {});
}

const cellClick = (column, row) => {
  emit('handleCellOperator', column.prop, row[column.prop], row);
};

const doLayout = () => {
  if(elDataTable.value) {
    elDataTable.value.doLayout();
  }
}

const init = () => {
  if (props.dataUrl !== '') {
    queryUrl.value = props.dataUrl;

    for(let key in routerStore.routeMap) {
      const menu = routerStore.routeMap[key];
      if (menu?.data?.url === queryUrl.value) {
        operators.value = menu.meta.buttons || [];
        break;
      }
    }
  } else {
    queryUrl.value = route.meta.url;
    operators.value = route.meta.buttons || [];
  }

  if (props.defaultSort && props.defaultSort.prop) {
    if (props.defaultSort.order === 'ascending') {
      pageRequest.value.sortField = props.defaultSort.prop
      pageRequest.value.sortType = 'asc'
    } else if (props.defaultSort.order === 'descending') {
      pageRequest.value.sortField = props.defaultSort.prop
      pageRequest.value.sortType = 'desc'
    } else {
      pageRequest.value.sortField = null
      pageRequest.value.sortType = null
    }
  }

  tableDataList.value = props.dataList || [];

  if (props.autoLoadPage) {
    findPage();
  }
}

watch(() => props.dataUrl, () => {
  init()
})

watch(() => props.dataList, val => {
  tableDataList.value = val;
})

watch(() => tableDataList.value, () => {
  nextTick(function() {
    setTimeout(doLayout, 500);
  });
});

onMounted(() => {
  init();
});

defineExpose({
  findPage,
  refreshPage
});
</script>

<style scoped>
.table-box {
  display: flex;
  flex-direction: column;
  flex-grow: 1;
  height: 100%;
}
.tool-bar {
  display: inline-block;
  width: 100%;
}
.tool-bar>div{
  display: inline-block;
}
.form-tool {
  float: left;
}
.form-tool .el-form .el-form-item,
.form-tool  .el-form .el-form-item {
  margin-bottom: 5px;
}
.form-tool :deep(.el-input),
.form-tool :deep(.el-select) {
  width: 150px!important;
  text-align: left;
}
.form-tool .el-form--inline .el-form-item {
  margin-right: 10px;
}
.form-tool :deep(.el-date-editor--daterange.el-input__inner) {
  max-width: 250px;
}
.form-tool :deep(.el-range-input) {
  max-width: 120px;
  text-align: left;
}
.operator-button :deep(.el-button) {
  margin-right: 10px;
}
.operator-button :deep(.el-button+.el-button) {
  margin-left: 0;
}
.upload-dialog :deep(.el-dialog__body) {
  padding: 0 40px 0 40px;
}
.el-button--text.danger {
  color: #ec4141;
}
.el-button--text.danger:hover {
  color: #ec4141;
}
.el-button--text.primary {
  color: #378ef0;
}
.el-button--text.primary:hover {
  color: #378ef0;
}
.data-table {
  flex-grow: 1;
  display: flex;
  flex-direction: column;
}
.data-table :deep(td .cell) {
  padding: 0 5px!important;
}
.data-table :deep(td.el-table-column--selection .cell) {
  padding: 0!important;
}
.data-table .button-box :deep(.el-button) {
  margin: 0 5px;
}
.el-table :deep(.cell.el-tooltip) > span {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.el-table .click-link {
  color: #0085f6;
  cursor:pointer;
}
.el-table :deep(.first-row) {
  color: #FF9764;
  font-weight: bold;
}
.el-table :deep(.el-table__fixed-right) {
  background: #FFFFFF;
}
.el-table :deep(.el-table__inner-wrapper) {
  height: 100px;
  flex-grow: 1;
}
.el-table__empty-block {
  min-height: 200px;
  display: flex;
  flex-direction: column;
}
.pagination-box {
  margin-top: 10px;
  display: flex;
}
.pagination-box-left {
  font-size: 14px;
  line-height: 32px;
  padding-right: 5px;
  color: red;
  flex: auto;
  overflow: hidden;
  text-overflow: ellipsis;
  flex-wrap: nowrap;
  white-space: nowrap;
  text-align: left;
}
.pagination-box-right {
  max-width: 100%;
  text-align: right;
  flex: 1 1 auto;
  flex-wrap: nowrap;
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
}
.pagination-box-right :deep(.el-pagination) {
  padding-right: 0;
}
.pagination-box-right :deep(.el-pagination__editor) {
  margin-right: 8px;
}
.pagination-box-right :deep(.is-last) {
  margin-left: 10px;
  margin-right: 0;
}
</style>
